import { flee, goTo } from '@/modules/move'
import { AttackTask } from '../types'
import { isNearEdge } from '@/utils/position'
import { calcBodyEffectiveness, calcCreepDamage, isEnemy } from '@/utils/war'

/**
 * attacker 工作
 */
export const attackerWork = (creeps: Creep[], task: AttackTask) => {
    creeps = creeps.filter((creep) => !creep.spawning)

    creeps.forEach((creep) => {
        delete creep.memory.stand

        if (creep.hits < creep.hitsMax) {
            creep.heal(creep)
        }

        // 还有爬未孵化完成
        if (!task.spawnComplete) {
            if (isNearEdge(creep.pos, 3)) return

            goTo(creep, new RoomPosition(25, 25, task.roomName), { range: 23 })
            return
        }

        const currentRoom = creep.room
        if (!currentRoom.enemys) {
            currentRoom.enemys = creep.room.find(FIND_HOSTILE_CREEPS).filter(isEnemy)
        }
        const enemys = currentRoom.enemys
        if (enemys.length) {
            // n格以内的敌人
            const enemysInRange5 = enemys.filter((enemy) => creep.pos.getRangeTo(enemy) <= 5)
            const enemysInRange4 = enemysInRange5.filter((enemy) => creep.pos.getRangeTo(enemy) <= 4)
            const closestEnemy = creep.pos.findClosestByRange(enemysInRange4.length ? enemysInRange4 : enemys)
            const closestRange = closestEnemy ? creep.pos.getRangeTo(closestEnemy) : 25
            // 有攻击力的敌人
            const enemyAttackers: { creep: Creep; attack: number }[] = []
            const attackPower = enemysInRange4.reduce((total, enemy) => {
                const range = creep.pos.getRangeTo(enemy) - (enemy.fatigue ? 0 : 1)
                const power =
                    (range <= 2 ? calcBodyEffectiveness(enemy.body, 'attack', 'attack', 30) : 0) +
                    (range <= 3 ? calcBodyEffectiveness(enemy.body, 'ranged_attack', 'rangedAttack', 10) : 0)
                if (power > 0) enemyAttackers.push({ creep: enemy, attack: power })
                return total + power
            }, 0)
            const damage = calcCreepDamage(creep, attackPower)
            const heal = calcBodyEffectiveness(creep.body, 'heal', 'heal', 12)
            const isDanger = damage > heal
            // 逃跑
            if (isDanger || creep.hits < creep.hitsMax) {
                flee(
                    creep,
                    enemysInRange4.map((enemy) => ({ pos: enemy.pos, range: 5 })),
                    addCosts(currentRoom, enemyAttackers),
                )
            } else if (closestEnemy) {
                // 能否前进，即假设前进看是否有危险
                const closerAttackPower = enemysInRange5.reduce((total, enemy) => {
                    const range = creep.pos.getRangeTo(enemy) - (enemy.fatigue ? 1 : 2)
                    const power =
                        (range <= 2 ? calcBodyEffectiveness(enemy.body, 'attack', 'attack', 30) : 0) +
                        (range <= 3 ? calcBodyEffectiveness(enemy.body, 'ranged_attack', 'rangedAttack', 10) : 0)
                    if (power > 0) enemyAttackers.push({ creep: enemy, attack: power })
                    return total + power
                }, 0)
                const closerDamage = calcCreepDamage(creep, closerAttackPower)
                if (closerDamage > heal) {
                    creep.memory.stand = true
                } else {
                    // 追最近的敌人
                    if (closestRange > 1) {
                        goTo(creep, closestEnemy.pos, { range: 0, maxRooms: 1 })
                    } else if (!closestEnemy.fatigue && !creep.fatigue) {
                        creep.move(creep.pos.getDirectionTo(closestEnemy.pos))
                        creep.memory.stand = true
                    }
                }
            }

            // 攻击
            if (closestRange <= 1) {
                creep.rangedMassAttack()
            } else if (closestRange <= 3) {
                closestEnemy && creep.rangedAttack(closestEnemy)
            }

            // 治疗
            if (enemyAttackers.length || creep.hits < creep.hitsMax) {
                creep.heal(creep)
            }
        } else if (currentRoom.name !== task.roomName) {
            if (creep.hits === creep.hitsMax) {
                goTo(creep, new RoomPosition(25, 25, task.roomName), { range: 23 })
            } else if (isNearEdge(creep.pos, 3)) {
                goTo(creep, new RoomPosition(25, 25, currentRoom.name), { range: 20 })
            }
        } else {
            if (!creep.memory.targetId) {
                if (!creep.memory.slack) {
                    const struct = creep.pos.findClosestByRange(
                        currentRoom
                            .find(FIND_STRUCTURES)
                            .filter((s) => s.hits && s.structureType !== 'road')
                            .sort((a, b) => a.hits - b.hits),
                    )
                    if (struct) {
                        creep.memory.targetId = struct.id
                    } else {
                        creep.memory.slack = 21
                        return
                    }
                }
            }

            if (!creep.memory.targetId) return

            const struct = Game.getObjectById(creep.memory.targetId) as Structure
            if (!struct || !struct.hits) {
                delete creep.memory.targetId
                return
            }

            const range = creep.pos.getRangeTo(struct)
            if (range <= 1) {
                if (
                    struct instanceof StructureWall ||
                    struct instanceof StructureRoad ||
                    struct instanceof StructureContainer
                ) {
                    creep.rangedAttack(struct)
                } else {
                    creep.rangedMassAttack()
                }
                creep.memory.stand = true
            } else {
                if (range <= 3) {
                    creep.rangedAttack(struct)
                    creep.memory.stand = true
                }
                goTo(creep, struct.pos, { range: 1 })
            }
        }
    })
}

function addCosts(room: Room, enemyAttackers: { creep: Creep; attack: number }[]) {
    return (costs: CostMatrix) => {
        const addCost = (item: Structure | ConstructionSite) => {
            // 更倾向走道路
            if (item.structureType === STRUCTURE_ROAD) {
                // 造好的路可以走，路的工地保持原有 cost
                if (item instanceof Structure) costs.set(item.pos.x, item.pos.y, 1)
            }
            // 不能穿过无法行走的建筑
            else if (
                item.structureType !== STRUCTURE_CONTAINER &&
                (item.structureType !== STRUCTURE_RAMPART || !item.my)
            ) {
                costs.set(item.pos.x, item.pos.y, 255)
            }
        }

        // 给建筑和工地添加 cost
        room.find(FIND_STRUCTURES).forEach(addCost)
        if (room.controller?.my) {
            room.find(FIND_CONSTRUCTION_SITES).forEach(addCost)
        }

        // 给敌人添加 cost
        for (const { creep, attack } of enemyAttackers) {
            for (let x = creep.pos.x - 3; x <= creep.pos.x + 3; x++) {
                for (let y = creep.pos.y - 3; y <= creep.pos.y + 3; y++) {
                    if (x < 0 || x > 49 || y < 0 || y > 49) continue
                    costs.set(x, y, Math.min(costs.get(x, y) + attack, 255))
                }
            }
        }

        return costs
    }
}
